package com.net.xpay.core.service.impl.auto;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.net.xpay.common.domain.User;
import com.net.xpay.core.constant.CoreConstant;
import com.net.xpay.core.constant.PayMethod;
import com.net.xpay.core.constant.PoseidonErrorCode;
import com.net.xpay.core.constant.TransBizTypeConstant;
import com.net.xpay.core.enums.*;
import com.net.xpay.core.model.*;
import com.net.xpay.core.validator.AutoPayValidator;
import com.net.common.annotation.WriteTransactional;
import com.net.common.exception.BusinessException;
import com.net.common.util.StreamUtil;
import com.net.xpay.core.domain.AlipayUser;
import com.net.xpay.core.domain.PayTrans;
import com.net.xpay.core.domain.PayTransBatch;
import com.net.xpay.core.domain.auto.AutoPayBatch;
import com.net.xpay.core.domain.auto.AutoPayOrder;
import com.net.xpay.core.domain.auto.AutoPayReceiptAccount;
import com.net.xpay.core.domain.auto.AutoPayTransBatch;
import com.net.xpay.core.enums.*;
import com.net.xpay.core.manager.AlipayUserManager;
import com.net.xpay.core.manager.PayTransBatchManager;
import com.net.xpay.core.manager.UserAccountManager;
import com.net.xpay.core.manager.auto.AutoPayBatchManager;
import com.net.xpay.core.manager.auto.AutoPayOrderManager;
import com.net.xpay.core.manager.auto.AutoPayReceiptAccountManager;
import com.net.xpay.core.manager.auto.AutoPayTransBatchManager;
import com.net.xpay.core.model.*;
import com.net.xpay.core.service.InnerPayTransService;
import com.net.xpay.core.service.PayTransBatchService;
import com.net.xpay.core.service.auto.AutoPayService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class AutoPayServiceImpl implements AutoPayService {
    @Autowired
    private AutoPayBatchManager autoPayBatchManager;

    @Autowired
    private AutoPayOrderManager autoPayOrderManager;

    @Autowired
    private AutoPayValidator autoPayValidator;

    @Autowired
    private AlipayUserManager alipayUserManager;

    @Autowired
    private UserAccountManager userAccountManager;

    @Autowired
    private InnerPayTransService innerPayTransService;

    @Autowired
    private AutoPayReceiptAccountManager autoPayReceiptAccountManager;

    @Autowired
    private PayTransBatchManager payTransBatchManager;

    @Autowired
    private AutoPayTransBatchManager autoPayTransBatchManager;

    @Autowired
    private PayTransBatchService payTransBatchService;

    @Override
    @WriteTransactional
    public AutoPayBatch ensureAutoPayBatch(User merchant, Long id) {
        AutoPayBatch autoPayBatch = autoPayBatchManager.findById(merchant.getId(), id);
        if (autoPayBatch.getStatus() != AutoPayBatchStatus.IMPORTED) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL,"该状态不支持该操作");
        }
        autoPayBatch.setStatus(AutoPayBatchStatus.ENSURED);
        autoPayBatchManager.updateByPrimaryKeyWithVersion(autoPayBatch);

        autoPayOrderManager.updateAutoPayOrderStatus(merchant.getId(), id, AutoPayOrderStatus.ENSURED);

        return autoPayBatch;
    }

    @Override
    @WriteTransactional
    public String autoPay(User user, CommonAutoPayModel payModel) {
        autoPayValidator.validateAutoPay(user, payModel);
        //查询付款支付宝是否已签约
        AlipayUser alipayUser = alipayUserManager.findByAlipayNickName(user.getId(), payModel.getAlipayNickName());
        if (alipayUser == null || alipayUser.getIsDeleted() == 1 || alipayUser.getStatus() != AlipayUserStatus.YES
        ) {
            throw new BusinessException(PoseidonErrorCode.ALIPAY_USER_NO_SIGN, "支付宝账号未签约或者已失效，请重新签约");
        }

        //对用户上锁 防止并发操作
        userAccountManager.getByIdWithLock(user.getId());
        //查询待转账的 条目 匹配金额转账
        List<AutoPayOrder> orders = matchAutoPayOrder(user, new BigDecimal(payModel.getTransAmount()), payModel);
        //生成转账订单 并生成转账链接
        return doTrans(user, alipayUser, payModel, orders);
    }

    @Override
    @WriteTransactional
    public void updatePayResult(User user, PayTransBatch batch, List<PayTrans> payTransList) {
        if (!batch.getImportBatchNo().startsWith(TransBizTypeConstant.YT_AT)) {
            log.error("updatePayResult error biz type wrong|importBatchNo={}|userId={}", batch.getImportBatchNo(), user.getId());
            return;
        }
        if (batch.getEndTime() == null) {
            return;
        }
        AutoPayTransBatch autoPayTransBatch = autoPayTransBatchManager.findByBizId(user.getId(), batch.getImportBatchNo());
        if (autoPayTransBatch == null) {
            log.error("updatePayResult error batch is not find userId={}|batchId={}", user.getId(), batch.getId());
            return;
        }

        autoPayTransBatch.setSuccessCount(batch.getSuccessCount());
        autoPayTransBatch.setSuccessAmount(batch.getSuccessAmount());
        autoPayTransBatch.setFailCount(batch.getFailCount());
        autoPayTransBatch.setFailedAmount(batch.getFailedAmount());
        autoPayTransBatch.setEndTime(batch.getEndTime());
        autoPayTransBatch.setStatus(AutoPayTransBatchStatus.of(batch.getStatus().getValue()));
        autoPayTransBatch.setBatchTransId(batch.getBatchTransId());
        autoPayTransBatch.setRefund(batch.getRefund());
        autoPayTransBatchManager.updateByPrimaryKeyWithVersion(autoPayTransBatch);

        List<AutoPayOrder> orders = autoPayOrderManager.findByAutoPayTransBatchId(user.getId(), autoPayTransBatch.getId());

        Map<String/*importBizNo*/, PayTrans> payTransMap = StreamUtil.toMap(payTransList, PayTrans::getImportBizNo);
        for (AutoPayOrder order : orders) {
            PayTrans payTrans = payTransMap.get(order.getId().toString());
            if (payTrans.getStatus() == PayTransStatus.SUCCESS) {
                order.setStatus(AutoPayOrderStatus.SUCCESS);
            } else {
                order.setStatus(AutoPayOrderStatus.FAIL);
                order.setErrorCode(payTrans.getErrorCode());
                order.setErrorMsg(payTrans.getErrorMsg());
            }
            order.setDetailId(payTrans.getDetailId());
            order.setEndTime(payTrans.getEndTime());
        }
        autoPayOrderManager.batchUpdate(orders);
    }

    @Override
    @WriteTransactional
    public void closeAutoPayTransBatch(User user, Long id) {
        log.info("closeAutoPayTransBatch userId={}|id={}", user.getId(), id);
        AutoPayTransBatch autoPayTransBatch = autoPayTransBatchManager.findById(user.getId(), id);

        if (autoPayTransBatch.getStatus() != AutoPayTransBatchStatus.WAIT_PAY) {
            log.warn("closeAutoPayTransBatch status error|id={}|userId={}|status={}",id, user.getId(), autoPayTransBatch.getStatus());
            return;
        }
        //五分钟过期
        if (autoPayTransBatch.getDealingTime().isAfter(LocalDateTime.now().minusMinutes(CoreConstant.AUTO_PAY_EXPIRED_TIME))) {
            return;
        }

        PayTransBatch payTransBatch = payTransBatchManager.findByImportBatchNo(user.getId(), autoPayTransBatch.getBizId());
        if (PayTransBatchStatus.isFinalStatus(payTransBatch.getStatus())) {
            log.warn("closeAutoPayTransBatch but pay batch status is final|id={}|userId={}|status={}",id, user.getId(), autoPayTransBatch.getStatus());
            return;
        }

        if (payTransBatch.getStatus() != PayTransBatchStatus.WAIT_PAY) {
            log.warn("closeAutoPayTransBatch but pay batch status not wait pay|id={}|userId={}|status={}",id, user.getId(), autoPayTransBatch.getStatus());
            return;
        }
        //内部最后会回调closeAutoPayTransBatchCallback方法关闭
        payTransBatchService.closePayTransBatch(user, payTransBatch.getId());
    }

    @Override
    public void closeAutoPayTransBatchCallback(User user, PayTransBatch payTransBatch) {
        if (!payTransBatch.getImportBatchNo().startsWith(TransBizTypeConstant.YT_AT)) {
            log.error("closeAutoPayTransBatchCallback error biz type wrong|importBatchNo={}|userId={}", payTransBatch.getImportBatchNo(), user.getId());
            return;
        }
        AutoPayTransBatch autoPayTransBatch = autoPayTransBatchManager.findByBizId(user.getId(), payTransBatch.getImportBatchNo());
        autoPayTransBatch.setStatus(AutoPayTransBatchStatus.DISUSE);
        autoPayTransBatch.setEndTime(LocalDateTime.now());
        autoPayTransBatch.setRefund(payTransBatch.getRefund());
        autoPayTransBatchManager.updateByPrimaryKeyWithVersion(autoPayTransBatch);

        List<AutoPayOrder> orders = autoPayOrderManager.findByAutoPayTransBatchId(user.getId(), autoPayTransBatch.getId());
        for (AutoPayOrder order : orders) {
            if (order.getIsSelf() == 1) {
                order.setStatus(AutoPayOrderStatus.DISUSE);
                order.setEndTime(LocalDateTime.now());
            } else {
                order.setStatus(AutoPayOrderStatus.ENSURED);
            }
        }
        autoPayOrderManager.batchUpdate(orders);
    }

    private String doTrans(User user, AlipayUser alipayUser, CommonAutoPayModel payModel, List<AutoPayOrder> orders) {
        //检验总金额防止不匹配
        Preconditions.checkArgument(new BigDecimal(payModel.getTransAmount()).compareTo(StreamUtil.sum(orders, AutoPayOrder::getTransAmount)) == 0);

        AutoPayTransBatch batch = AutoPayTransBatch.getInstance();
        batch.setUserId(user.getId());
        batch.setPartnerId(user.getPartnerId());
        batch.setFatherPartnerId(user.getFatherPartnerId());
        batch.setStatus(AutoPayTransBatchStatus.INIT);
        batch.setDealingTime(LocalDateTime.now());
        batch.setRemark(payModel.getRemark());
        batch.setAlipayUserId(alipayUser.getId());
        batch.setAlipayNickName(payModel.getAlipayNickName());
        batch.setTotalCount((long) orders.size());
        batch.setTotalTransAmount(StreamUtil.sum(orders, AutoPayOrder::getTransAmount).toString());
        autoPayTransBatchManager.save(batch);

        //发起转账
        InnerPayTransBatchModel payTransBatchModel = new InnerPayTransBatchModel();
        payTransBatchModel.setAlipayNickName(payModel.getAlipayNickName());
        payTransBatchModel.setMobile(user.getMobile());
        payTransBatchModel.setImportBatchNo(batch.getBizId());
        List<PayTransModel> payTrans = new ArrayList<>();

        BigDecimal sum = new BigDecimal(0);
        for (AutoPayOrder order : orders) {
            PayTransModel payTransModel = new PayTransModel();
            payTransModel.setImportBizNo(order.getId().toString());
            payTransModel.setTransAmount(order.getTransAmount().toString());
            payTransModel.setIdentity(order.getIdentity());
            payTransModel.setName(order.getName());
            payTransModel.setRemark(order.getRemark());
            payTrans.add(payTransModel);
            sum = sum.add(order.getTransAmount());
        }

        payTransBatchModel.setPayTrans(payTrans);
        payTransBatchModel.setPayMethod(PayMethod.MOBILE_QR_CODE);

        String payUrl = innerPayTransService.batchPayTrans(payTransBatchModel);
        if (Strings.isNullOrEmpty(payUrl)) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "系统繁忙，打款失败");
        }
        PayTransBatch payTransBatch = payTransBatchManager.findByImportBatchNo(user.getId(), batch.getBizId());
        if (payTransBatch == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "系统失败，打款失败");
        }

        batch.setStatus(AutoPayTransBatchStatus.WAIT_PAY);
        batch.setMode(payTransBatch.getMode());
        batch.setServiceRatio(payTransBatch.getServiceRatio());
        batch.setServiceUnitPrice(payTransBatch.getServiceUnitPrice());
        batch.setServicePrice(payTransBatch.getServicePrice());
        batch.setRefund(payTransBatch.getRefund());
        batch.setTotalTransAmount(payTransBatch.getTotalTransAmount());
        batch.setTotalCount(payTransBatch.getTotalCount());
        batch.setSuccessAmount(payTransBatch.getSuccessAmount());
        batch.setSuccessCount(payTransBatch.getSuccessCount());
        batch.setFailedAmount(payTransBatch.getFailedAmount());
        batch.setFailCount(payTransBatch.getFailCount());
        batch.setBatchTransId(payTransBatch.getBatchTransId());
        batch.setLastPayUrl(payTransBatch.getLastPayUrl());
        batch.setLastMobilePayUrl(payTransBatch.getLastMobilePayUrl());
        batch.setMobilePayQrCode(payTransBatch.getMobilePayQrCode());

        autoPayTransBatchManager.updateByPrimaryKeyWithVersion(batch);

        orders.forEach(order -> {
            order.setAutoPayTransBatchId(batch.getId());
            order.setStatus(AutoPayOrderStatus.WAIT_PAY);
            order.setDealingTime(LocalDateTime.now());
        });
        autoPayOrderManager.batchUpdate(orders);

        return payUrl;
    }

    private AutoPayReceiptAccount queryAutoPayReceiptAccount(Long userId, CommonAutoPayModel payModel) {
        AutoPayReceiptAccountSearchModel searchModel = new AutoPayReceiptAccountSearchModel();
        searchModel.setPageSize(2L);
        searchModel.setUserId(userId);
        searchModel.setIsDeleted(0);
        searchModel.setColumn("receipt_count");
        searchModel.setOrderby("asc");
        List<AutoPayReceiptAccount> accounts = autoPayReceiptAccountManager.search(searchModel);
        if (CollectionUtils.isEmpty(accounts)) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "至少需要配置一个收款账号");
        }

        AutoPayReceiptAccount account = accounts.get(0);
        if (!account.getIdentity().equals(payModel.getAlipayNickName())) {
            return account;
        }

        if (accounts.size() == 1) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "无法打款给自己，请至少配置2个自收款账号");
        }

        return accounts.get(1);
    }

    private List<AutoPayOrder> matchAutoPayOrder(User user, BigDecimal transAmount, CommonAutoPayModel payModel) {
        //先尝试能不能不预留钱匹配
        List<AutoPayOrder> orders = tryMatchAutoPayOrder(user, transAmount, payModel);
        if (!CollectionUtils.isEmpty(orders)) {
            return orders;
        }
        //预留一块钱匹配
        return finalMatchAutoPayOrder(user, transAmount.subtract(new BigDecimal(1)), payModel);
    }

    private List<AutoPayOrder> finalMatchAutoPayOrder(User user, BigDecimal transAmount, CommonAutoPayModel payModel) {
        Long userId = user.getId();

        AutoPayOrderSearchModel searchModel = new AutoPayOrderSearchModel();
        searchModel.setUserId(userId);
        searchModel.setStatus(AutoPayOrderStatus.ENSURED);
        searchModel.setMaxTransAmount(transAmount);
        searchModel.setIsSelf(0);
        searchModel.setColumn("create_time");
        searchModel.setOrderby("asc");
        searchModel.setPageSize(200L);//一批次最多不能超过200笔
        List<AutoPayOrder> selectedOrders = autoPayOrderManager.search(searchModel);

        BigDecimal sumAmount = new BigDecimal(0);
        List<AutoPayOrder> targetOrders = new ArrayList<>();
        for (AutoPayOrder autoPayOrder : selectedOrders) {
            Preconditions.checkArgument(autoPayOrder.getStatus() == AutoPayOrderStatus.ENSURED);
            Preconditions.checkArgument(autoPayOrder.getUserId().equals(userId));

            if (sumAmount.add(autoPayOrder.getTransAmount()).compareTo(transAmount) < 0) {
                sumAmount = sumAmount.add(autoPayOrder.getTransAmount());
                targetOrders.add(autoPayOrder);
            } else if (sumAmount.add(autoPayOrder.getTransAmount()).compareTo(transAmount) == 0) {
                sumAmount = sumAmount.add(autoPayOrder.getTransAmount());
                targetOrders.add(autoPayOrder);
                break;
            }
        }
        //查询自配置账号
        AutoPayReceiptAccount autoPayReceiptAccount = queryAutoPayReceiptAccount(userId, payModel);
        autoPayReceiptAccount.setReceiptCount(autoPayReceiptAccount.getReceiptCount() + 1L);
        autoPayReceiptAccountManager.updateByPrimaryKeyWithVersion(autoPayReceiptAccount);
        //创建一个自收款订单
        BigDecimal receiptMoney = transAmount.subtract(sumAmount).add(new BigDecimal(1));//要加上预留的一块钱
        AutoPayOrder order = AutoPayOrder.getInstance();
        order.setUserId(userId);
        order.setPartnerId(user.getPartnerId());
        order.setFatherPartnerId(user.getFatherPartnerId());
        order.setStatus(AutoPayOrderStatus.ENSURED);
        order.setIsSelf(1);
        order.setTransAmount(receiptMoney);
        order.setRemark("结余转入");
        order.setIdentity(autoPayReceiptAccount.getIdentity());
        order.setIdentityType(autoPayReceiptAccount.getIdentityType());
        order.setName(autoPayReceiptAccount.getName());
        autoPayOrderManager.save(order);

        targetOrders.add(order);

        return targetOrders;
    }

    private List<AutoPayOrder> tryMatchAutoPayOrder(User user, BigDecimal transAmount, CommonAutoPayModel payModel) {
        Long userId = user.getId();

        AutoPayOrderSearchModel searchModel = new AutoPayOrderSearchModel();
        searchModel.setUserId(userId);
        searchModel.setStatus(AutoPayOrderStatus.ENSURED);
        searchModel.setMaxTransAmount(transAmount);
        searchModel.setIsSelf(0);
        searchModel.setColumn("create_time");
        searchModel.setOrderby("asc");
        searchModel.setPageSize(200L);
        List<AutoPayOrder> selectedOrders = autoPayOrderManager.search(searchModel);

        BigDecimal sumAmount = new BigDecimal(0);
        List<AutoPayOrder> targetOrders = new ArrayList<>();
        for (AutoPayOrder autoPayOrder : selectedOrders) {
            Preconditions.checkArgument(autoPayOrder.getStatus() == AutoPayOrderStatus.ENSURED);
            Preconditions.checkArgument(autoPayOrder.getUserId().equals(userId));

            if (sumAmount.add(autoPayOrder.getTransAmount()).compareTo(transAmount) < 0) {
                sumAmount = sumAmount.add(autoPayOrder.getTransAmount());
                targetOrders.add(autoPayOrder);
            } else if (sumAmount.add(autoPayOrder.getTransAmount()).compareTo(transAmount) == 0) {
                sumAmount = sumAmount.add(autoPayOrder.getTransAmount());
                targetOrders.add(autoPayOrder);
                break;
            }
        }
        BigDecimal receiptMoney = transAmount.subtract(sumAmount);
        if (receiptMoney.compareTo(new BigDecimal(0)) == 0) {
            return targetOrders;
        }
        if (receiptMoney.compareTo(new BigDecimal(1)) < 0) {
            return new ArrayList<>();//剩余金额b小于1元 匹配失败
        }

        //查询自配置账号
        AutoPayReceiptAccount autoPayReceiptAccount = queryAutoPayReceiptAccount(userId, payModel);
        autoPayReceiptAccount.setReceiptCount(autoPayReceiptAccount.getReceiptCount() + 1L);
        autoPayReceiptAccountManager.updateByPrimaryKeyWithVersion(autoPayReceiptAccount);
        //创建一个自收款订单
        AutoPayOrder order = AutoPayOrder.getInstance();
        order.setUserId(userId);
        order.setPartnerId(user.getPartnerId());
        order.setFatherPartnerId(user.getFatherPartnerId());
        order.setStatus(AutoPayOrderStatus.ENSURED);
        order.setIsSelf(1);
        order.setTransAmount(receiptMoney);
        order.setRemark("结余转入");
        order.setIdentity(autoPayReceiptAccount.getIdentity());
        order.setIdentityType(autoPayReceiptAccount.getIdentityType());
        order.setName(autoPayReceiptAccount.getName());
        autoPayOrderManager.save(order);

        targetOrders.add(order);

        return targetOrders;
    }
}
